package com.edgarli.gamble.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.edgarli.common.utils.ArithUtil;
import com.edgarli.gamble.domain.TLottoHistory;
import com.edgarli.gamble.mapper.TLottoHistoryMapper;
import com.edgarli.gamble.service.ITLottoHistoryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.edgarli.gamble.mapper.TLottoMapper;
import com.edgarli.gamble.domain.TLotto;
import com.edgarli.gamble.service.ITLottoService;

/**
 * 大乐透Service业务层处理
 *
 * @author EdgarLi
 * @date 2024-08-14
 */
@Service
@Slf4j
public class TLottoServiceImpl extends ServiceImpl<TLottoMapper, TLotto> implements ITLottoService {
    @Autowired
    private TLottoMapper tLottoMapper;

    @Autowired
    private TLottoHistoryMapper tLottoHistoryMapper;

    @Autowired
    private ITLottoHistoryService tLottoHistoryService;

    /**
     * 查询大乐透
     *
     * @param lottoId 大乐透主键
     * @return 大乐透
     */
    @Override
    public TLotto selectTLottoByLottoId(Long lottoId) {
        return tLottoMapper.selectTLottoByLottoId(lottoId);
    }

    /**
     * 查询大乐透列表
     *
     * @param tLotto 大乐透
     * @return 大乐透
     */
    @Override
    public List<TLotto> selectTLottoList(TLotto tLotto) {
        return tLottoMapper.selectTLottoList(tLotto);
    }

    /**
     * 批量删除大乐透
     *
     * @param lottoIds 需要删除的大乐透主键
     * @return 结果
     */
    @Override
    public int deleteTLottoByLottoIds(Long[] lottoIds) {
        return tLottoMapper.deleteTLottoByLottoIds(lottoIds);
    }

    /**
     * 删除大乐透信息
     *
     * @param lottoId 大乐透主键
     * @return 结果
     */
    @Override
    public int deleteTLottoByLottoId(Long lottoId) {
        return tLottoMapper.deleteTLottoByLottoId(lottoId);
    }

    @Override
    public void estimate() {
        ExecutorService executorService = Executors.newCachedThreadPool();
        LambdaQueryWrapper<TLottoHistory> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.orderByDesc(TLottoHistory::getPeriods).last("limit 1");
        TLottoHistory lottoHistory = tLottoHistoryService.getOne(queryWrapper);
        int periods = Integer.valueOf(lottoHistory.getPeriods()) + 1;
        executorService.execute(() -> {
            firstEstimate(periods, 0, "first");
            log.info("第一种方式预估");
        });
        executorService.execute(() -> {
            firstEstimate(periods, 150, "Second");
            log.info("第一种方式预估：进150 条");
        });
        executorService.execute(() -> {
            randomLotto(periods);
            log.info("随机预估");
        });

    }

    /**
     * 根据出现次数查询出现概率
     *
     * @param items
     * @return
     */
    public static Map<String, Integer> probabilityOfListW(List<String> items) {
        Map<String, Integer> map = new HashMap<>();
        if (items == null || items.size() == 0) {
            return map;
        }
        for (String k : items) {
            Integer count = map.get(k);
            map.put(k, (count == null) ? 1 : count + 1);
        }
        return sortMap(map);
    }

    /**
     * 根据map 中的value 进行排序 (正序)
     * @param map
     * @return
     */
    public static Map<String, Integer> sortMap(Map<String, Integer> map) {
        //利用Map的entrySet方法，转化为list进行排序
        List<Map.Entry<String, Integer>> entryList = new ArrayList<>(map.entrySet());
        //利用Collections的sort方法对list排序
        Collections.sort(entryList, new Comparator<Map.Entry<String, Integer>>() {
            @Override
            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                //正序排列，倒序反过来
                return o1.getValue() - o2.getValue();
            }
        });
        //遍历排序好的list，一定要放进LinkedHashMap，因为只有LinkedHashMap是根据插入顺序进行存储

        return listForMap(entryList);
    }

    /**
     * list<map<String,Intenger>> 转map
     * @param entryList
     * @return
     */
    public static Map<String, Integer> listForMap(List<Map.Entry<String, Integer>> entryList) {

        //遍历排序好的list，一定要放进LinkedHashMap，因为只有LinkedHashMap是根据插入顺序进行存储
        LinkedHashMap<String, Integer> linkedHashMap = new LinkedHashMap<String, Integer>();
        for (Map.Entry<String, Integer> e : entryList) {
            linkedHashMap.put(e.getKey(), e.getValue());
        }
        return linkedHashMap;
    }


    /**
     * 查询所有结果，从结果种预测数据
     */
    private void firstEstimate(int periods, int limit, String type) {
        List<TLottoHistory> allHistory = new ArrayList<>();
        if (limit == 0) {
            allHistory = tLottoHistoryMapper.selectList(new LambdaQueryWrapper<TLottoHistory>());
        } else {
            allHistory = tLottoHistoryMapper.selectList(new LambdaQueryWrapper<TLottoHistory>().orderByDesc(TLottoHistory::getKjrqDt).last("limit " + limit));
        }
        int num = allHistory.size();//总次数
        //获取 每次出现的次数
        List<String> n1List = allHistory.stream().map(TLottoHistory::getN1).collect(Collectors.toList());
        Map<String, Integer> map1Num = probabilityOfListW(n1List);
        List<String> n2List = allHistory.stream().map(TLottoHistory::getN2).collect(Collectors.toList());
        Map<String, Integer> map2Num = probabilityOfListW(n2List);
        List<String> n3List = allHistory.stream().map(TLottoHistory::getN3).collect(Collectors.toList());
        Map<String, Integer> map3Num = probabilityOfListW(n3List);
        List<String> n4List = allHistory.stream().map(TLottoHistory::getN4).collect(Collectors.toList());
        Map<String, Integer> map4Num = probabilityOfListW(n4List);
        List<String> n5List = allHistory.stream().map(TLottoHistory::getN5).collect(Collectors.toList());
        Map<String, Integer> map5Num = probabilityOfListW(n5List);
        List<String> n6List = allHistory.stream().map(TLottoHistory::getN6).collect(Collectors.toList());
        Map<String, Integer> map6Num = probabilityOfListW(n6List);
        List<String> n7List = allHistory.stream().map(TLottoHistory::getN7).collect(Collectors.toList());
        Map<String, Integer> map7Num = probabilityOfListW(n7List);

        //map 合并成一个list 前 后 个一个
        Map<String, Integer> mergedMap1 = Stream.of(map1Num, map2Num, map3Num, map4Num, map5Num).flatMap(map -> map.entrySet().stream()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v1 + v2));
//        System.out.println(sortMap(mergedMap1));

        Map<String, Integer> mergedMap2 = Stream.of(map6Num, map7Num).flatMap(map -> map.entrySet().stream()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v1 + v2));
//        System.out.println(sortMap(mergedMap2));

        //1.1 取汇总后最小的前五和后二 数据
        firstEstimate_one(sortMap(mergedMap1), sortMap(mergedMap2), periods, type + "_one");
        //1.2 取汇总后最大的前五和后二 数据
        firstEstimate_Last(sortMap(mergedMap1), sortMap(mergedMap2), periods, type + "_last");
    }

    private void firstEstimate_one(Map<String, Integer> mergedMap1, Map<String, Integer> mergedMap2, int periods, String type) {
        TLotto tLotto = new TLotto();
        Map<String, Integer> leftMap = listForMap(new ArrayList<>(mergedMap1.entrySet()).subList(0, 5));
        List<String> leftKeyLists = new ArrayList<>(leftMap.keySet());
        tLotto.setN1(leftKeyLists.get(0)).setN2(leftKeyLists.get(1)).setN3(leftKeyLists.get(2)).setN4(leftKeyLists.get(3)).setN5(leftKeyLists.get(4));

        Map<String, Integer> rightMap = listForMap(new ArrayList<>(mergedMap2.entrySet()).subList(0, 2));
        List<String> rightKeyLists = new ArrayList<>(rightMap.keySet());
        tLotto.setN6(rightKeyLists.get(0)).setN7(rightKeyLists.get(1));

        int allLeftAdd = mergedMap1.values().stream().mapToInt(Integer::intValue).sum();
        int leftAdd = leftMap.values().stream().mapToInt(Integer::intValue).sum();

        int allRightAdd = mergedMap2.values().stream().mapToInt(Integer::intValue).sum();
        int rightAdd = rightMap.values().stream().mapToInt(Integer::intValue).sum();
        Collections.sort(leftKeyLists);
        Collections.sort(rightKeyLists);
        String join = String.join(" ", leftKeyLists) + " " + String.join(" ", rightKeyLists);
        tLotto.setNumber(join);
        BigDecimal div = ArithUtil.div(ArithUtil.add(leftAdd, rightAdd), ArithUtil.add(allLeftAdd, allRightAdd));
        tLotto.setYgAccuracy(div);
        tLotto.setYgrqDt(new Date());
        tLotto.setPeriods(String.valueOf(periods));
        tLotto.setType(type);
        tLotto.setRemark("取汇总后最小的前五和后二");

        LambdaQueryWrapper<TLotto> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TLotto::getPeriods, tLotto.getPeriods()).eq(TLotto::getNumber, tLotto.getNumber());
        TLotto one = this.getOne(wrapper);
        if (one == null) {
            tLottoMapper.insert(tLotto);
        }
    }

    private void firstEstimate_Last(Map<String, Integer> mergedMap1, Map<String, Integer> mergedMap2, int periods, String type) {
        TLotto tLotto = new TLotto();
        Map<String, Integer> leftMap = listForMap(new ArrayList<>(mergedMap1.entrySet()).subList(30, 35));
        List<String> leftKeyLists = new ArrayList<>(leftMap.keySet());
        tLotto.setN1(leftKeyLists.get(0)).setN2(leftKeyLists.get(1)).setN3(leftKeyLists.get(2)).setN4(leftKeyLists.get(3)).setN5(leftKeyLists.get(4));

        Map<String, Integer> rightMap = listForMap(new ArrayList<>(mergedMap2.entrySet()).subList(8, 10));
        List<String> rightKeyLists = new ArrayList<>(rightMap.keySet());
        tLotto.setN6(rightKeyLists.get(0)).setN7(rightKeyLists.get(1));

        int allLeftAdd = mergedMap1.values().stream().mapToInt(Integer::intValue).sum();
        int leftAdd = leftMap.values().stream().mapToInt(Integer::intValue).sum();

        int allRightAdd = mergedMap2.values().stream().mapToInt(Integer::intValue).sum();
        int rightAdd = rightMap.values().stream().mapToInt(Integer::intValue).sum();
        Collections.sort(leftKeyLists);
        Collections.sort(rightKeyLists);
        String join = String.join(" ", leftKeyLists) + " " + String.join(" ", rightKeyLists);
        tLotto.setNumber(join);
        BigDecimal div = ArithUtil.div(ArithUtil.add(leftAdd, rightAdd), ArithUtil.add(allLeftAdd, allRightAdd));
        tLotto.setYgAccuracy(div);
        tLotto.setYgrqDt(new Date());
        tLotto.setPeriods(String.valueOf(periods));
        tLotto.setType(type);
        tLotto.setRemark("取汇总后最大的前五和后二；前排序" + leftMap + "后排序：" + rightMap);

        LambdaQueryWrapper<TLotto> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TLotto::getPeriods, tLotto.getPeriods()).eq(TLotto::getNumber, tLotto.getNumber());
        TLotto one = this.getOne(wrapper);
        if (one == null) {
            tLottoMapper.insert(tLotto);
        }
    }


    private void randomLotto(int periods) {
        List<String> q = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            boolean w = true;
            while (w) {
                String n = randomNum(1, 35);
                if (q.contains(n)) {
                    w = true;
                } else {
                    q.add(n);
                    w = false;
                }
            }
        }
        List<String> h = new ArrayList<>();
        for (int i = 0; i < 2; i++) {
            boolean w = true;
            while (w) {
                String n = randomNum(1, 12);
                if (q.contains(n)) {
                    w = true;
                } else {
                    h.add(n);
                    w = false;
                }
            }
        }
        TLotto tLotto = new TLotto();
        tLotto.setN1(q.get(0));
        tLotto.setN2(q.get(1));
        tLotto.setN3(q.get(2));
        tLotto.setN4(q.get(3));
        tLotto.setN5(q.get(4));
        tLotto.setN6(h.get(0));
        tLotto.setN7(h.get(1));

        String join = String.join(" ", q) + " " + String.join(" ", h);
        tLotto.setNumber(join);
        tLotto.setYgAccuracy(BigDecimal.ZERO);
        tLotto.setYgrqDt(new Date());
        tLotto.setPeriods(String.valueOf(periods));
        tLotto.setType("random");
        tLotto.setRemark("随机排序");

        LambdaQueryWrapper<TLotto> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TLotto::getPeriods, tLotto.getPeriods()).eq(TLotto::getNumber, tLotto.getNumber());
        TLotto one = this.getOne(wrapper);
        if (one == null) {
            tLottoMapper.insert(tLotto);
        } else {
            randomLotto(periods);
        }
    }

    private String randomNum(int min, int max) {
        Random random = new Random();
        int x = random.nextInt(max - min + 1) + min;
        return String.format("%0" + 2 + "d", x);
    }
}
